// use biome_console::fmt::Formatter;
// use biome_console::markup;
// use biome_diagnostics::{Category, Diagnostic, DiagnosticTags, Location, Severity, category};
// use biome_rowan::{SyntaxError, TextRange};
use std::error::Error;

use super::{TextRange, prelude::TagKind};

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
/// Series of errors encountered during formatting
pub enum FormatError {
    /// In case a node can't be formatted because it either misses a require child element or
    /// a child is present that should not (e.g. a trailing comma after a rest element).
    SyntaxError,
    /// In case range formatting failed because the provided range was larger
    /// than the formatted syntax tree
    RangeError { input: TextRange, tree: TextRange },

    /// In case printing the document failed because it has an invalid structure.
    InvalidDocument(InvalidDocumentError),

    /// Formatting failed because some content encountered a situation where a layout
    /// choice by an enclosing [crate::Format] resulted in a poor layout for a child [crate::Format].
    ///
    /// It's up to an enclosing [crate::Format] to handle the error and pick another layout.
    /// This error should not be raised if there's no outer [crate::Format] handling the poor layout error,
    /// avoiding that formatting of the whole document fails.
    PoorLayout,
}

impl std::fmt::Display for FormatError {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FormatError::SyntaxError => {
                fmt.write_str("Can't format code because it contains syntax errors")
            }
            FormatError::RangeError { input, tree } => {
                std::write!(fmt, "Formatting range {input:?} is larger than syntax tree {tree:?}")
            }
            FormatError::InvalidDocument(error) => std::write!(
                fmt,
                "Invalid document: {error}\n\n This is an internal Biome error. Please report if necessary."
            ),
            FormatError::PoorLayout => fmt.write_str(
                "Poor layout: The formatter wasn't able to pick a good layout for your document. This is an internal Biome error. Please report if necessary.",
            ),
        }
    }
}

impl Error for FormatError {}

// impl From<SyntaxError> for FormatError {
// fn from(error: SyntaxError) -> Self {
// FormatError::from(&error)
// }
// }

// impl From<&SyntaxError> for FormatError {
// fn from(syntax_error: &SyntaxError) -> Self {
// match syntax_error {
// SyntaxError::MissingRequiredChild
// | SyntaxError::UnexpectedBogusNode
// | SyntaxError::UnexpectedMetavariable => FormatError::SyntaxError,
// }
// }
// }

impl From<PrintError> for FormatError {
    fn from(error: PrintError) -> Self {
        FormatError::from(&error)
    }
}

impl From<&PrintError> for FormatError {
    fn from(error: &PrintError) -> Self {
        match error {
            PrintError::InvalidDocument(reason) => FormatError::InvalidDocument(*reason),
        }
    }
}

// impl Diagnostic for FormatError {
// fn location(&self) -> Location<'_> {
// Location::builder().build()
// }

// fn severity(&self) -> Severity {
// Severity::Error
// }

// fn tags(&self) -> DiagnosticTags {
// match self {
// FormatError::SyntaxError => DiagnosticTags::empty(),
// FormatError::RangeError { .. } => DiagnosticTags::empty(),
// FormatError::InvalidDocument(_) => DiagnosticTags::INTERNAL,
// FormatError::PoorLayout => DiagnosticTags::INTERNAL,
// }
// }

// fn description(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// std::fmt::Display::fmt(self, fmt)
// }

// fn category(&self) -> Option<&'static Category> {
// Some(category!("format"))
// }

// fn message(
// &self,
// fmt: &mut biome_diagnostics::console::fmt::Formatter<'_>,
// ) -> std::io::Result<()> {
// match self {
// FormatError::SyntaxError => fmt.write_str("Syntax error."),
// FormatError::RangeError { input, tree } => {
// std::write!(fmt, "Formatting range {input:?} is larger than syntax tree {tree:?}")
// }
// FormatError::InvalidDocument(error) => std::write!(fmt, "Invalid document: {error}"),
// FormatError::PoorLayout => {
// fmt.write_str(
// "Poor layout: The formatter wasn't able to pick a good layout for your document."
// )
// }
// }
// }
// }

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum InvalidDocumentError {
    /// Mismatching start/end kinds
    ///
    /// ```plain
    /// StartIndent
    /// ...
    /// EndGroup
    /// ```
    StartEndTagMismatch { start_kind: TagKind, end_kind: TagKind },

    /// End tag without a corresponding start tag.
    ///
    /// ```plain
    /// Text
    /// EndGroup
    /// ```
    StartTagMissing { kind: TagKind },

    /// Expected a specific start tag but instead is:
    /// * at the end of the document
    /// * at another start tag
    /// * at an end tag
    ExpectedStart { expected_start: TagKind, actual: ActualStart },
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ActualStart {
    /// The actual element is not a tag.
    Content,

    /// The actual element was a start tag of another kind.
    Start(TagKind),

    /// The actual element is an end tag instead of a start tag.
    End(TagKind),

    /// Reached the end of the document
    EndOfDocument,
}

impl std::fmt::Display for InvalidDocumentError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InvalidDocumentError::StartEndTagMismatch { start_kind, end_kind } => {
                std::write!(f, "Expected end tag of kind {start_kind:?} but found {end_kind:?}.")
            }
            InvalidDocumentError::StartTagMissing { kind } => {
                std::write!(f, "End tag of kind {kind:?} without matching start tag.")
            }
            InvalidDocumentError::ExpectedStart { expected_start, actual } => match actual {
                ActualStart::EndOfDocument => {
                    std::write!(
                        f,
                        "Expected start tag of kind {expected_start:?} but at the end of document."
                    )
                }
                ActualStart::Start(start) => {
                    std::write!(
                        f,
                        "Expected start tag of kind {expected_start:?} but found start tag of kind {start:?}."
                    )
                }
                ActualStart::End(end) => {
                    std::write!(
                        f,
                        "Expected start tag of kind {expected_start:?} but found end tag of kind {end:?}."
                    )
                }
                ActualStart::Content => {
                    std::write!(
                        f,
                        "Expected start tag of kind {expected_start:?} but found non-tag element."
                    )
                }
            },
        }
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PrintError {
    InvalidDocument(InvalidDocumentError),
}

impl Error for PrintError {}

impl std::fmt::Display for PrintError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PrintError::InvalidDocument(inner) => {
                std::write!(f, "Invalid document: {inner}")
            }
        }
    }
}

// impl Diagnostic for PrintError {
// fn category(&self) -> Option<&'static Category> {
// Some(category!("format"))
// }

// fn severity(&self) -> Severity {
// Severity::Error
// }

// fn message(&self, fmt: &mut Formatter<'_>) -> std::io::Result<()> {
// match self {
// PrintError::InvalidDocument(inner) => {
// let inner = format!("{inner}");
// fmt.write_markup(markup! {
// "Invalid document: "{{inner}}
// })
// }
// }
// }
// }
